package com.tufang.core;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.RuntimeUtil;
import com.tufang.bean.DbColumn;
import com.tufang.bean.TableModel;
import com.tufang.config.GenConfig;
import com.tufang.constants.Constants;
import com.tufang.util.DbUtils;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import sun.security.jca.GetInstance;

import java.io.File;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代码生成器核心类
 * @author: KevinOy
 * @date: 2019/03/17 13:14
 */
public class CodeGenerator {

    private static CodeGenerator codeGenerator = new CodeGenerator();

    private GenConfig config;
    private Connection conn;
    private String[] tableNames;

    private CodeGenerator() {}

    public static CodeGenerator GetInstance(GenConfig gcConfig) {
        codeGenerator.config = gcConfig;
        codeGenerator.init();
        return codeGenerator;
    }

    /**
     * 初始化
     */
    private void init() {
        //表名处理
        tableNames = config.getTableName().split(",");
        conn = DbUtils.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
        if (conn == null) {
            System.out.println("[代码生成器]->数据库连接获取失败");
            System.exit(1);
        }
    }

    /**
     * 代码生成主方法
     * @throws Exception
     */
    public void generate() throws Exception {
        if (config == null) {
            System.out.println("[代码生成器]->配置文件为空");
            return;
        }
        System.out.println("代码生成器开始运行......");
        init();
        for (int i = 0; i < tableNames.length; i++) {
            String tableName = tableNames[i];
            TableModel tableModel = getTableModel(tableName);
            generateEntity(tableModel);
            generateMapper(tableModel);
            generateMapperXml(tableModel);
            //windows系统自动打开文件目录
            RuntimeUtil.exec("explorer " + config.getOutputPath());
        }
    }

    /**
     * 获得表信息
     * @param tableName
     * @return 表信息
     */
    protected TableModel getTableModel(String tableName) {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            Map<String, Object> commentMap = getCommentMap(tableName);//注释map
            String sql = "select * from " + tableName;
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            List<DbColumn> columnList = new ArrayList<>();
            Map<String, String> importPackageMap = new HashMap<String, String>();
            for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                DbColumn dbColumn = new DbColumn();
                String columnDbName = rsmd.getColumnName(i).toLowerCase();
                dbColumn.setColumnDbName(columnDbName.toUpperCase());//db字段名
                String columnJavaName = getColumnJavaName(columnDbName);
                dbColumn.setColumnJavaName(columnJavaName);//java字段名(首字母小写)
                dbColumn.setColumnJavaNameFirstUpper(columnJavaName.substring(0, 1).toUpperCase() + columnJavaName.substring(1));//java字段名(首字母大写)
                dbColumn.setColumnDbType(rsmd.getColumnTypeName(i));//db字段类型
                dbColumn.setColumnJavaType(getColumnJavaType(rsmd.getColumnTypeName(i).toUpperCase()));//java字段类型
                dbColumn.setComment(String.valueOf(commentMap.get(columnDbName)));//注释
                importPackageMap.put(dbColumn.getColumnJavaType(), "1");//导包类型集合
                columnList.add(dbColumn);
            }
            TableModel tableModel = new TableModel();
            tableModel.setTableName(tableName);
            tableModel.setColumnList(columnList);
            tableModel.setImportPackageMap(importPackageMap);
            tableModel.setPackageName(config.getPackageName());
            tableModel.setClassName(getJavaClassName(tableName));
            return tableModel;
        } catch (Exception e) {
            System.out.println("获取列信息错误");
            e.printStackTrace();
        } finally {
            DbUtils.close(conn, ps, rs);//最后操作数据库方法，关闭conn连接等信息
        }
        return null;
    }

    /**
     * 生成实体
     * @param tableModel 表Model
     */
    protected void generateEntity(TableModel tableModel) throws Exception {
        Map<String, Object> map = initParamMap();
        map.put("packageName", config.getPackageName());
        map.put("className", tableModel.getClassName());
        map.put("tableName", tableModel.getTableName());
        map.put("columnList", tableModel.getColumnList());
        map.put("importPackageMap", tableModel.getImportPackageMap());
        String renderStr = renderTemplate(map, "entity.btl");//页面str

        String filePath = config.getOutputPath() + "/"
                + Constants.SRC_MAIN_JAVA + "/" + config.getPackageName().replace(".", "/")
                + "/entity/" + tableModel.getClassName() + ".java";//文件名
        System.out.println("Entity生成路径:" + filePath);
        File file = FileUtil.touch(filePath);//创建目录,父目录不存在也自动创建
        FileWriter writer = new FileWriter(file);
        writer.write(renderStr);
    }

    /**
     * 生成MapperXML
     * @param tableModel 表Model
     */
    protected void generateMapper(TableModel tableModel) throws Exception {
        Map<String, Object> map = initParamMap();
        map.put("packageName", config.getPackageName());
        map.put("className", tableModel.getClassName());
        map.put("tableName", tableModel.getTableName());
        map.put("columnList", tableModel.getColumnList());
        map.put("importPackageMap", tableModel.getImportPackageMap());
        map.put("baseColumnList", getBaseColumnList(tableModel.getColumnList()));//Base Column List
        String renderStr = renderTemplate(map, "mapper.btl");//页面str

        String filePath = config.getOutputPath() + "/"
                + Constants.SRC_MAIN_JAVA + "/" + config.getPackageName().replace(".", "/")
                + "/mapper/" + tableModel.getClassName() + "Mapper.java";//文件名
        System.out.println("mapper接口生成路径:" + filePath);
        File file = FileUtil.touch(filePath);//创建目录,父目录不存在也自动创建
        FileWriter writer = new FileWriter(file);
        writer.write(renderStr);
    }

    /**
     * 生成MapperXML
     * @param tableModel 表Model
     * @void
     */
    protected void generateMapperXml(TableModel tableModel) throws Exception {
        Map<String, Object> map = initParamMap();
        map.put("packageName", config.getPackageName());
        map.put("className", tableModel.getClassName());
        map.put("tableName", tableModel.getTableName());
        map.put("columnList", tableModel.getColumnList());
        map.put("importPackageMap", tableModel.getImportPackageMap());
        map.put("baseColumnList", getBaseColumnList(tableModel.getColumnList()));//Base Column List
        String renderStr = renderTemplate(map, "mapperXml.btl");//页面str

        String filePath = config.getOutputPath() + "/"
                + Constants.SRC_MAIN_RESOURCES + "/" + config.getModuleName().replace(".", "/")
                + "/" + tableModel.getClassName() + "Mapper.xml";//文件名
        System.out.println("mapperXml生成路径:" + filePath);
        File file = FileUtil.touch(filePath);//创建目录,父目录不存在也自动创建
        FileWriter writer = new FileWriter(file);
        writer.write(renderStr);
    }

    /**
     * 获得BaseColumn字段名称
     * @param columns
     * @return MapperXML中的BaseColumnList公共列信息
     */
    protected String getBaseColumnList(List<DbColumn> columns) {
        String str = "";
        for (int i = 0; i < columns.size(); i++) {
            if (i == columns.size() - 1) {
                str += columns.get(i).getColumnDbName();
                break;
            } else {
                str += columns.get(i).getColumnDbName() + ", ";
            }
        }
        return str;
    }


    /**
     * 获得注释
     * @param tableName 表名
     * @return 注释Map，Key为字段名称，小写，Value为注释信息
     */
    protected Map<String, Object> getCommentMap(String tableName) throws Exception {
        String sql = "show full columns from " + tableName;
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        Map<String, Object> map = new HashMap<String, Object>();
        while (rs.next()) {
            map.put(rs.getString("Field").toLowerCase(), rs.getString("Comment"));
        }
        DbUtils.close(ps, rs);
        return map;
    }

    /**
     * 获得java属性名
     * @param dbName 字段在数据库中名称
     * @return Entity中字段名称，首字母大写，驼峰命名法
     */
    protected String  getColumnJavaName(String dbName) {
        String columnName = dbName;
        if (config.isUnderLine2Camel() && dbName.indexOf("_") != -1) {
            String mergeName = "";
            String[] strs = dbName.split("_");
            for (int i = 0; i <strs.length; i++) {
                String str = strs[i];
                if (i > 0) {
                    mergeName += str.substring(0, 1).toUpperCase() + str.substring(1);
                } else {
                    mergeName += str;
                }
            }
            columnName = mergeName;
        }
        return columnName;
    }




    /**
     * 渲染模板
     * @param paramMap 参数
     * @param templateName 模板名称
     * @return 渲染后文本
     */
    protected String renderTemplate(Map<String, Object> paramMap, String templateName) throws Exception {
        ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(config.getTemplatePath());
        Configuration cfg = Configuration.defaultConfiguration();
        GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
        Template t = gt.getTemplate(templateName);
        t.binding(paramMap);
        String str = t.render();
        return str;
    }

    /**
     * 初始化渲染参数
     */
    protected Map<String, Object> initParamMap() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("author", config.getAuthor());
        map.put("packageName", config.getPackageName());
        map.put("moduleName", config.getModuleName());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm");
        map.put("dataStr", sdf.format(new java.util.Date()));
        return map;
    }

    /**
     * 获得类名
     * @param tableName 表名
     * @return Java类名，如UserMapper中的User
     */
    protected String getJavaClassName(String tableName) {
        String javaClassName = "";
        String prefix = config.getTablePrefix();

        if (prefix != null && prefix != "" && tableName.indexOf("_") != -1) {
            String[] strs = tableName.split("_");
            for (int i = 1; i < strs.length; i++) {
                String str = strs[i];
                javaClassName += str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
            }
            javaClassName = javaClassName.substring(0, 1).toUpperCase() + javaClassName.substring(1);
        } else {
            javaClassName = tableName.substring(0, 1).toUpperCase() + tableName.substring(1);
        }
        return javaClassName;
    }


    /**
     * 获得Java字段类型
     * @param dbType 字段在数据库中的类型
     * @return Java类型
     */
    protected String getColumnJavaType(String dbType) {
        String str = Constants.mysqlTypeMap.get(dbType);
        if ("".equals(str)) {
            throw new RuntimeException("不支持的字段类型[" + dbType + "]");
        }
        return str;
    }

}
